﻿#include<stdio.h>
/*

指针是什么？
指针理解的2个要点：
1. 指针是内存中一个最小单元的编号，也就是地址
2. 平时口语中说的指针，通常指的是指针变量，是用来存放内存地址的变量
总结：指针就是地址，口语中说的指针通常指的是指针变量

*/

//int main()
//{
//	int a = 10;//a是整型变量，占用4个字节的内存空间      a的地址展示的是初始字节的地址
//	int* pa = &a;
//
//	//pa是一个指针变量，用来存放地址的
//
//	//pa
//
//
//	//本质上指针就是地址
//	//口语中说的指针，其实是指针变量，指针变量就是一个变量，指针变量是用来存放地址的一个变量
//
//	return 0;
//}

//X86 - 32位的环境
//X64 - 64位的环境

//int main()
//{
//	char* pc = NULL;
//	short* ps = NULL;
//	int* pi = NULL;
//	double* pd = NULL;
//
//	//ptr_t pt = NULL;
//
//	//sizeof 返回的值的类型是无符号整型  unsigned int
//	printf("%zu\n", sizeof(pc));
//	printf("%zu\n", sizeof(ps));
//	printf("%zu\n", sizeof(pi));
//	printf("%zu\n", sizeof(pd));
//
//	return 0;
//}

//0 1 2 3 4 5 6 7 8 9 a b c d e f
//0
//1
//10
//11
//100
//1010
//1111
//


//int main()
//{
//	int a = 0x11223344;
//	//int* pa = &a;
//	//*pa = 0;
//	
//	char* pc = (char*)&a;//int*
//	*pc = 0;
//
//	//结论：
//	//指针类型决定了指针在被解引用的时候访问几个字节
//	//如果是int*的指针，解引用访问4个字节
//	//如果是char*的指针，解引用访问1个字节
//	//推广到其他类型
//	//
//	return 0;
//}

//指针类型的意义

//int main()
//{
//	int a = 0x11223344;
//	int*  pa = &a;
//	char* pc = (char*)&a;
//
//	printf("pa = %p\n", pa);
//	printf("pa+1 = %p\n", pa+1);
//
//	printf("pc = %p\n", pc);
//	printf("pc+1 = %p\n", pc+1);
//
//	//结论2：
//	//指针的类型决定了指针+-1操作的时候，跳过几个字节
//	//决定了指针的步长
//
//	//100 - 10进制
//	//0x64 - 16进制
//	//110 0100 - 2进制
//	//144 - 8进制
//	return 0;
//}
//


//int main()
//{
//	int a = 0;
//
//	int* pi = &a;//pi 解引用访问4个字节，pi+1也是跳过4个字节
//	float* pf = &a;//pf 解引用访问4个字节，pf+1也是跳过4个字节
//	//int* 和 float* 是不是就可以通用啊？
//	//不能
//	//*pi = 100;
//	*pf = 100.0;
//
//	return 0;
//}


/*
野指针： 野指针就是指针指向的位置是不可知的（随机的、不正确的、没有明确限制的）

1. 指针未初始化

2. 指针越界访问

3. 指针指向的空间释放

避免：
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放，及时置NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
*/





//int main()
//{
//	int arr[10] = { 0 };
//	int* p = arr;//&arr[0]   arr数组名表示首元素地址等价于&arr[0]
//	int i = 0;
//	for (i = 0; i <= 10; i++)
//	{
//		*p = i;
//		p++;             //p++表示arr++（首元素地址++，访问第二个元素的地址）
//	}
//
//	return 0;
//}



//
//int main()
//{
//	int a = 0x11223344;
//	int b = 287454020;
//
//	return 0;
//}
//

//
//int main()
//{
//	int* p;
//	//p没有初始化，就意味着没有明确的指向
//	//一个局部变量不初始化的化，放的是随机值：0xcccccccc
//	//
//	*p = 10;//非法访问内存了，这里的p就是野指针
//
//	return 0;
//}
//





//指针运算

/*
1.指针+-整数

//*arr++(让arr++，（*arr）++是指让（*arr即所指向的对象）加加）      *arr++==1即*arr=1；arr++
//数组随着下标的增长，地址由低到高变化


#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数；指针的关系运算（地址比较大小）
for (vp = &values[0]; vp < &values[N_VALUES];)
{
     *vp++ = 0; //后置，先使用，后++
}

2.指针-指针

3.指针的关系运算
*/


//int main()
//{
//	int arr[10] = {0};
//	int i = 0;
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	//数组下标的写法
//	//for (i = 0; i < sz; i++)
//	//{
//	//	arr[i] = 1;
//	//}
//
//	/*int* p = arr;
//	for (i = 0; i < sz; i++)
//	{
//		*p = 1;
//		p++;
//	}*/
//
//	//int* p = arr;
//	//for (i = 0; i < sz; i++)
//	//{
//	//	*(p + i) = 1;                //i自己++，直接使用i
//	//}
//
//	return 0;
//}
//
//int main()
//{
//	int arr[10] = { 0 };
//	int i = 0;
//	int sz = sizeof(arr) / sizeof(arr[0]);
//
//	for (i = 0; i < sz; i++)
//	{
//		*(arr+i) = 1;  //也可以！arr是首元素地址，解引用来赋值。arr+i让地址递增  //需要一个递增的数的时候，可以直接用i
//		//*arr = 1;         //arr++是arr=arr+1  但是arr就是首元素地址，不能更改，所以错了！
//		//arr++;          不对，表达式必须是可修改的左值，也就是说arr就是arr，不可修改
//	}
//	return 0;
//}
//




//指针-指针：的绝对值得到的是指针与指针之间元素的个数
//不是所有指针都能相减，指向同一块空间的两个指针才能相减


//int main()
//{
//	//int arr[10] = { 0 };
//	//printf("%d\n", &arr[0] - &arr[9]);//?         9-0=9    反过来是-9
//	// &arr【0】才是地址，arr【0】只是一个元素
//	// 
//	//int arr[10] = { 0 };
//	//char ch[5] = {0};
//	//printf("%d\n", &ch[0] - &arr[5]);//err
//
//
//	return 0;
//}


#include <string.h>


/// 1
//int my_strlen(char* str)
//{
//	int count = 0;
//	while (*str != '\0')
//	{
//		count++;
//		str++;
//	}
//
//	return count;
//}
//2. 递归的版本

//3. 指针-指针
//int my_strlen(char* str)     //指针变量接收
//{
//	char* start = str;
//	while (*str != '\0')
//	{
//		str++;
//	}
//	return (str - start）;           //0的地址-首个元素的地址
//}
//
//int main()
//{
//	int len = my_strlen("abcdef");
//	printf("%d\n", len);
//
//	return 0;
//}


/*


for(vp = &values[N_VALUES]; vp > &values[0];)      //指针地址高低的比较  指针先--后使用
{
    *--vp = 0;           //没有控制语句，依靠循环体进行控制
}

//最好正着，从小开始初始化
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)        // vp >= &values[0]   地址高低之间的判断，注意是&arr【0】  //数组元素要加上&
{
    *vp = 0;      //从values【4】开始==0，然后进入循环体，再vp--

}

实际在绝大部分的编译器上是可以顺利完成任务的，然而我们还是应该避免这样写，因为标准并不保证它可行。


标准规定
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较，但是不允许与
指向第一个元素之前的那个内存位置的指针进行比较。
*/

//数组：一组相同类型元素的集合
//指针变量：是一个变量，存放的是地址

//int main()
//{
//	int arr[10] = {0};
//	//arr 是首元素的地址
//	//&arr[0]
//	int* p = arr;
//	//通过指针来访问数组
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int i = 0;
//
//	for (i = 0; i < sz; i++)
//	{
//		printf("%p ----- %p\n", &arr[i], p + i);
//	}
//
//	//for (i = 0; i < sz; i++)
//	//{
//	//	printf("%d ", *(p + i));
//	//}
//
//	return 0;
//}


//void test(int* p, int sz)   //传上首元素地址，用指针变量接收     for条件用分号，函数参数用逗号
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//}

//void test(int arr[], int sz)    //直接用数组接收
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", arr[i]);// 本质还是 arr[i]--> *(arr+i)
//	}
//}
//
//int main()
//{
//	int arr[10] = {0};
//	test(arr, 10);
//
//	return 0;
//}

